﻿using MathNet.Numerics;
using MathNet.Numerics.Statistics;
using SeeSharpTools.JY.DSP.Fundamental;
using SeeSharpTools.JY.DSP.Utility.Filter1D;
using Sunny.UI;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;

namespace SeeSharpTools.Demo
{
    public partial class FormSmooth : UIPage
    {
        public FormSmooth()
        {
            InitializeComponent();

            uiComboBox.SelectedIndex = 0;
        }

        private void uiComboBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            double fs = 1000;
            double[] t = Generate.LinearRange(0, 1 / fs, 0.5 - 1 / fs);
            double[] x = t.Select(num => Math.Cos(2 * Math.PI * 50 * num)).ToArray();
            double[] noise = new double[x.Length];
            Generation.UniformWhiteNoise(ref noise, 0.5);
            x = x.Zip(noise, (a, b) => a + b).ToArray();
            easyChartX1.Plot(t, x);

            double[] y;
            if (uiComboBox.SelectedIndex == 0)
            {
                y = MovMean(x, 5);
            }
            else
            {
                y = FIRFiltering.SgolayFilter(x, 3);
            }

            easyChartX2.Plot(t, y);
        }

        /// <summary>
        /// 移动平均运算，返回由局部 windowSize 个数据点的均值组成的数组，其中每个均值是基于相邻元素的长度为 windowSize的滑动窗计算得出。
        /// 窗口长度为奇数时，以当前位置的元素为中心。窗口长度为偶数时，以当前元素及其前一个元素为中心。
        /// 当没有足够的元素填满窗口时，窗口将自动在端点处截断。当窗口被截断时，只根据窗口内的元素计算平均值。
        /// </summary>
        /// <param name="x">输入信号</param>
        /// <param name="windowSize">窗口长度，默认值为3</param>
        /// <param name="discardEndPoints">是否舍弃端点计算，仅返回满窗口平均值</param>
        /// <returns></returns>
        public static double[] MovMean(double[] x, int windowSize = 3, bool discardEndPoints = false)
        {
            double[] weights = Generate.Repeat(windowSize, 1.0);
            double[] sma = Convolve(x, weights).Select(num => num / windowSize).ToArray();
            if (!discardEndPoints)
            {
                int left = (int)Math.Floor(windowSize / 2.0);
                List<double> sma_left = new List<double>();
                for (int i = 0; i < left; i++)
                {
                    sma_left.Add(x.Take(windowSize - left + i).Mean());
                }
                int right = (int)Math.Floor((windowSize - 1) / 2.0);
                List<double> sma_right = new List<double>();
                for (int i = 0; i < right; i++)
                {
                    sma_right.Add(x.Skip(x.Length - windowSize + i + 1).Mean());
                }
                sma = sma_left.Concat(sma).Concat(sma_right).ToArray();
            }
            return sma;
        }

        /// <summary>
        /// 计算两个一维实数序列的线性卷积
        /// </summary>
        /// <param name="x">第一个输入序列</param>
        /// <param name="y">第二个输入序列</param>
        /// <returns>卷积结果</returns>
        public static double[] Convolve(double[] x, double[] y)
        {
            if (x.Length == 0 || y.Length == 0) throw new Exception("不能为空数组");

            int n = x.Length;
            int m = y.Length;
            int length = m + n - 1;
            double[] cxy = new double[length];

            int index;
            for (int i = 0; i < length; i++)
            {
                for (int k = 0; k < n; k++)
                {
                    index = i - k;
                    if ((index >= 0) && (index < m))
                    {
                        cxy[i] += x[k] * y[index];
                    }
                }
            }
            return Centered(cxy, Math.Max(n, m) - Math.Min(n, m) + 1);
        }

        /// <summary>
        /// 获取原数组指定长度的中心部分
        /// </summary>
        /// <param name="x"></param>
        /// <param name="shape"></param>
        /// <returns></returns>
        public static T[] Centered<T>(T[] x, int shape)
        {
            if (x.Length <= shape) return x;

            int startIndex = (x.Length - shape) / 2;
            int endIndex = startIndex + shape;

            T[] y = new T[endIndex - startIndex];
            Array.Copy(x, startIndex, y, 0, y.Length);
            return y;
        }
    }
}